#!/usr/bin/python

import re
import datetime
import sys
import pdb
import logging

class rule:
    def __init__(self, n, desc, re_am, re_pl, re_dt, def_am):
        self.name = n
        self.desc = desc
        self.re_amount = re.compile(re_am, re.S)
        self.default_am = def_am
        if re_pl != '':
            self.re_place = re.compile(re_pl, re.S)
        else:
            self.re_place = None
        if re_dt != '':
            self.re_date = re.compile(re_dt, re.S)
        else:
            self.re_date = None

rules = [
    rule('C', 'chase cc', 'charge of\s+.*USD..([0-9\.]+)', 
              ' at (.{3,20}) has been authorized',
              'has\s+been\s+authorized\s+on\s+(.*)\s+E[SD]T.', 0),
    rule('C', 'chase cc', 'we are notifying you of a gas station charge', 
              ' was authorized at (.{3,20}) on',
              ' on (.*) E[SD]T.', 56.44),
    rule('C', 'atm', '[aA]tm ([0-9]+)', '([aA]tm)', '', 0),
    rule('C', 'food', '[fF]ood ([0-9\.]+)', '([fF]ood)', '', 0),
    rule('T', 'test', 'test ([0-9\.]+)', '', '', 0),
    rule('F', 'ftp used', 'Total size:.([0-9\.]+)', '', '', 0),
    rule('M', 'mail sent', 'sent:.([0-9\.]*)', '', '', 0)
]

atags = '''
acenet inc.,home
advanced dermat,health
amazon mktplace pmts,shop
amazon.com,shop
anna belen,shop
banana republic,shop
carter\'s,shop
century twenty on,shop
chikurin japanese,fun
cuisine,fun
cuisine,fun
ubisoft,shop
plimoth,fun
grille,fun
cuny,home
parksreserv,fun
expedia,fun
fabric,home
delta air,misc
domino,food
duane reade,shop
cvs pharmacy,shop
dsw,shop
ethnic television...,home
ezpass,car
fine fare superma...,food
gap usa,shop
glatt mart,food
barnes & noble,shop
radioshack,shop
parkreserva,fun
kodak,home
aaa,car
godiva chocolatie...,fun
gourmet plaza,food
cafe r,food
cgm 835 ave llc,food
fresh & co,food
fine fare,food
h & m,shop
ikea brooklyn,shop
jcpenney,shop
j&r,home
kohl\'s,shop
macy*s,shop
ocean view optica...,health
panera bread,fun
riding,fun
ruslan liquors,fun
sbux,fun
skype,comm
staples,home
sweik corp,fun
the childrens pla...,shop
thechildrenspla,shop
the shubert,fun
t j maxx,shop
tjmaxx,shop
toys r us,shop
victoria secret,shop
usps,home
sewpatterns,home
rite aid,home
soups llc,food
harrington,fun
marshalls,shop
crazy8,shop
the home depot,home
prspct prk z,fun
old navy usa,shop
longwood gardens,fun
tj maxx,shop
cherry hill,food
full pet service,home
auto parts,car
mc ski shop,fun
mc snowbown,fun
ikea,home
exxonmobil,car
sunoco,car
chase platin,chase
citi platin,citi
con edison,bilz
keysp energy,bilz
verizon,bilz
atm,cash
food,food
petco,home
chiffon,food
mta vending machine,home
valero fuel,car
walgreens,shop
stop & shop,food
amazon,shop
net cost market,food
best buy,home
2516 65th st,car
stokrotka,food
shoprite,food
suffern animal ho,shop
'''

def load_atags():
    list = []
    lines = atags.strip().split('\n')
    for line in lines:
        cols = line.split(',')
        list.append((cols[0], cols[1]))
    return list

atags = load_atags()

def get_atag(p):
    if p is None:
        return ''
    p = p.lower()
    for atag in atags:
        if p.find(atag[0]) >= 0:
            return atag[1]
    else:
        return ''

def parse_common(s):
    parsed = False
    name = 'common'
    amount = 0.0
    atag = ''
    date = None
    month = ''
    place = ''
    refnum = ''
    if s is None:
        return [(parsed, name, amount, place, atag, date, month, refnum)]

    for rule in rules:
        m = rule.re_amount.search(s)
 #       pdb.set_trace()
        if not m:
            continue
        if m.lastgroup is None and rule.default_am > 0:
            amount = rule.default_am
        else:
            g = m.group(1)
            amount = float(g)
        name = rule.name
        place = ''
        date = None
        if rule.re_place:
            m = rule.re_place.search(s)
            if m:
                place = m.group(1).replace('\n', '-')
        if rule.re_date:
            m = rule.re_date.search(s)
            if m:
                t = m.group(1).replace('\n', ' ').replace('\r', '').rstrip()
                date = datetime.datetime.strptime(t, '%m/%d/%Y %I:%M:%S %p')
        else:
            date = datetime.datetime.today()

        if date is None:
            month = None
        else:
            month = datetime.datetime.strftime(date, '%Y-%m')
        atag = get_atag(place)
        parsed = True
        if atag == '':
            parsed = False
        if name != 'C':
            parsed = True
        if parsed:
            break
    return [(parsed, name, amount, place, atag, date, month, refnum)]

rex_citi_bill = re.compile(
                           'Your payment to (.{3,20}) '
                           'for \$([0-9,\.]+),\s+'
                           'Reference\s+Number\s+([0-9]+),\s+'
                           'cleared\s+on\s+([0-9/]+)',
                           re.S)

rex_citi_check = re.compile('Check\s+#([0-9]+)\s+'
                            'for\s+\$([0-9,\.]+)\s+'
                            'was\s+presented\s+for\s+payment\s+on\s+([0-9/]+).')

def parse_citi(s):
    parsed = False
    name = 'citi'
    amount = 0.0
    place = ''
    atag = ''
    date = None
    month = '2000-01'
    refnum = ''
    n = s.find('Cleared Payment')
    list = []
    if n > 0:
        m = rex_citi_bill.search(s, n)
        while not m is None:
            name = 'payment'
            place = m.group(1)
            atag = get_atag(place)
            amount = float(m.group(2).replace(',', ''))
            refnum = m.group(3)
            date = datetime.datetime.strptime(m.group(4), '%m/%d/%y')
            month = datetime.datetime.strftime(date, '%Y-%m')
            list.append((True, name, amount, place, atag, date, month, refnum))
            n = s.find('Bill Payment', m.end())
            if n == -1:
                break 
            m = rex_citi_bill.search(s, n)
     
    n = s.find('Check Presented')
    if n > 0:
        m = rex_citi_check.search(s, n)
        while not m is None:
            name = 'check'
            place = 'bank'
            atag = 'bilz'
            amount = float(m.group(2).replace(',', ''))
            refnum = m.group(1)
            date = datetime.datetime.strptime(m.group(3), '%m/%d/%y')
            month = datetime.datetime.strftime(date, '%Y-%m')
            list.append((True, name, amount, place, atag, date, month, refnum))
            n = s.find('Bill Payment', m.end())
            if n == -1:
                break 
            m = rex_citi_bill.search(s, n)

    if len(list) > 0:
        return list
    else:   
        return [(parsed, name, amount, place, atag, date, month, refnum)]

def parse_date(s):
    try:
        date = datetime.datetime.strptime(s[0:25], '%a, %d %b %Y %H:%M:%S')
        offset = int(s[26:29]) * 60 + int(s[30:31])
        t = date - datetime.timedelta(minutes = offset)
    except:
        logging.error('Cannot parse data [%s]. Using current.' % s)
        t = datetime.datetime.utcnow()
    return t

re_email_address = re.compile('<([^>]+)>')

def parse_email_address(s):
    m = re_email_address.search(s)
    email = ''
    if m:
        return m.group(1)
    return s  

def parse(s):
    error = 'error'
    try:
        is_citi = (s.find('Citi') >= 0 and s.find('Account Balance') >= 0)
        is_citi |= s.find('Check Presented') >= 0
        is_citi |= s.find('Cleared Payment') >= 0

        if is_citi:
            return parse_citi(s)
        else:
            return parse_common(s)
    except:
        tb = sys.exc_info()[2]
        while tb:
            logging.error('error at %s:%s' % (tb.tb_frame.f_code.co_filename, tb.tb_lineno))
            tb = tb.tb_next
        error = 'parse error'

    return [(False, 'common', 0.0, error, '', None, '???', '')]

if __name__ == "__main__":

    f = open('citi~.txt', 'rt')
    s = f.read()
    f.close()

    for r in parse(s):
        print r

